
#ifndef _TTG_MATH_VEC3_
#define _TTG_MATH_VEC3_
#include "Common/Definitions.h"

#ifdef NVIDIA_PHYSX
#undef min
#undef max
#include "NxVec3.h"
#endif

#ifdef MICROSOFT_DIRECTX
#include <d3dx9math.h>
#endif

#include <math.h>
#include "Vector.h"

namespace ttg
{
	namespace math
	{
		template <class T> class vec3;
		template <class T> class vec4;

		template <class T>
		TTG_INLINE T dotProduct(const vec3<T> TTG_AMPERSAND a, const vec3<T> TTG_AMPERSAND b)
		{
			return a.x * b.x + a.y * b.y + a.z * b.z;
		}

		template <class T>
		TTG_INLINE vec3<T> crossProduct(const vec3<T> TTG_AMPERSAND a, const vec3<T> TTG_AMPERSAND b)
		{
			return vec3<T>(a.y * b.z - a.z * b.y,
						   -(a.x * b.z - a.z * b.x),
						   a.x * b.y - a.y * b.x);
		}

		template <class T>
		class vec3
		{
			public:
				static const vec3<T> ZERO;
				static const vec3<T> DEFAULT_DIRECTION;
			public:
				T x;
				T y;
				T z;

				//Constructors

				TTG_INLINE vec3<T>()
				{
					this->x = 0;
					this->y = 0;
					this->z = 0;
				}

				template <class T2>
				TTG_INLINE vec3<T>(const vec3<T2> TTG_AMPERSAND vec)
				{
					this->x = vec.x;
					this->y = vec.y;
					this->z = vec.z;
				}

				template <class T2>
				TTG_INLINE vec3<T>(const vec4<T2> TTG_AMPERSAND vec)
				{
					this->x = vec.x;
					this->y = vec.y;
					this->z = vec.z;
				}

		#ifdef MICROSOFT_DIRECTX

				TTG_INLINE vec3<T>(const D3DXVECTOR3 TTG_AMPERSAND vec)
				{
					this->x = vec.x;
					this->y = vec.y;
					this->z = vec.z;
				}

				TTG_INLINE vec3<T>(const D3DXVECTOR4 TTG_AMPERSAND vec)
				{
					this->x = (T)vec.x;
					this->y = (T)vec.y;
					this->z = (T)vec.z;
				}

		#endif

		#ifdef NVIDIA_PHYSX

				TTG_INLINE vec3<T>(const NxVec3 TTG_AMPERSAND vec)
				{
					this->x = (T)vec.x;
					this->y = (T)vec.y;
					this->z = (T)vec.z;
				}

		#endif
			    
				template <class T2>
				TTG_INLINE vec3<T>(T2 x, T2 y, T2 z)
				{
					this->x = (T)x;
					this->y = (T)y;
					this->z = (T)z;
				}

				//Operators
			    
				TTG_INLINE vec3<T> operator -() const
				{
					return vec3<T>(-this->x,
								   -this->y,
								   -this->z);
				}
			    
				template <class T2>
				TTG_INLINE vec3<T> operator -(const vec3<T2> TTG_AMPERSAND vec) const
				{
					return vec3(this->x - (T)vec.x,
								this->y - (T)vec.y,
								this->z - (T)vec.z);
				}
			    
				template <class T2>
				TTG_INLINE vec3<T> operator +(const vec3<T2> TTG_AMPERSAND vec) const
				{
					return vec3<T>(this->x + (T)vec.x,
								   this->y + (T)vec.y,
								   this->z + (T)vec.z);
				}
			    
				template <class T2>
				TTG_INLINE vec3<T> operator *(T2 a) const
				{
					return vec3<T>(this->x * (T)a,
								   this->y * (T)a,
								   this->z * (T)a);
				}
			    
				template <class T2>
				TTG_INLINE vec3<T> operator *(const vec3<T2> TTG_AMPERSAND vec) const
				{
					return vec3<T>(this->x * (T)vec.x,
								   this->y * (T)vec.y,
								   this->z * (T)vec.z);
				}

				template <class T2>
				TTG_INLINE vec3<T> operator /(T2 a) const
				{
					return vec3<T>(this->x / (T)a,
								   this->y / (T)a,
								   this->z / (T)a);
				}
			    
				template <class T2>
				TTG_INLINE vec3<T> operator /(const vec3<T2> TTG_AMPERSAND vec) const
				{
					return vec3<T>(this->x / (T)vec.x,
								   this->y / (T)vec.y,
								   this->z / (T)vec.z);
				}
			    
				template <class T2>
				TTG_INLINE const vec3<T> &operator =(const vec3<T2> TTG_AMPERSAND vec)
				{
					this->x = (T)vec.x;
					this->y = (T)vec.y;
					this->z = (T)vec.z;
					return *this;
				}
			    
				template <class T2>
				TTG_INLINE const vec3<T> operator +=(const vec3<T2> TTG_AMPERSAND vec)
				{
					this->x += (T)vec.x;
					this->y += (T)vec.y;
					this->z += (T)vec.z;
					return *this;
				}
			    
				template <class T2>
				TTG_INLINE const vec3<T> operator -=(const vec3<T2> TTG_AMPERSAND vec)
				{
					this->x -= (T)vec.x;
					this->y -= (T)vec.y;
					this->z -= (T)vec.z;
					return *this;
				}
			    
				template <class T2>
				TTG_INLINE bool operator ==(const vec3<T2> TTG_AMPERSAND vec) const
				{
					return (fabs(this->x - vec.x) + 
							fabs(this->y - vec.y) + 
							fabs(this->z - vec.z))
							< epsilon * 3;
				}

				template <class T2>
				TTG_INLINE bool operator !=(const vec3<T2> TTG_AMPERSAND vec) const
				{
					return (fabs(this->x - vec.x) + 
							fabs(this->y - vec.y) + 
							fabs(this->z - vec.z)
							>= epsilon * 3);
				}
			    
				TTG_INLINE T &operator[](const size_t n)
				{
					return (&x)[n];
				}
			    
				TTG_INLINE const T &operator[](const size_t n) const
				{
					return (&x)[n];
				}

				TTG_INLINE T getLength() const
				{
					return (T)sqrtf((float)(x * x + y * y + z * z));
				}

				TTG_INLINE vec3<T> normalize() const
				{
					T len = this->getLength();
		#ifdef TTG_DEBUG
					if (isZero(len))
					{
						   return vec3<T>(0, 0, 0);
					}
					else
		#endif
						return vec3<T>(x / len, y / len, z / len);
				}

				TTG_INLINE vec3<T> invert() const
				{
		#ifdef TTG_DEBUG
					if (isZero(this->x) || isZero(this->y) || isZero(this->y))
						return vec3<T>(0, 0, 0);
		#endif
					return vec3<T>(1 / x, 1 / y, 1 / z);
				}

				TTG_INLINE T project(const vec3<T> TTG_AMPERSAND ort) const
				{
					return dotProduct(*this, ort);
				}

				template <class T2>
				TTG_INLINE operator vec3<T2>() const
				{
					return vec3<T2>(x, y, z);
				}

				template <class T2>
				TTG_INLINE operator vec4<T2>() const
				{
					return vec4<T2>(x, y, z, 1);
				}

		#ifdef MICROSOFT_DIRECTX

				TTG_INLINE operator D3DXVECTOR3() const
				{
					return D3DXVECTOR3(x, y, z);
				}

				TTG_INLINE operator D3DXVECTOR4() const
				{
					return D3DXVECTOR4(x, y, z, 1);
				}

		#endif

		#ifdef NVIDIA_PHYSX

				TTG_INLINE operator NxVec3() const
				{
					return NxVec3(this->x, this->y, this->z);
				}

		#endif
		};
	}
}

#endif
